import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import javax.swing.JOptionPane;
import javax.swing.event.ChangeListener;

/**
 * @author Team Name: ByteCode
 * Team members: Kevin Conner, Vinesh Thakur, and James Yu
 * MancalaDataModel class holds all the data for the mancala game
 */
public class MancalaDataModel {
   
   public ArrayList<ChangeListener> arrayList;
   HashMap<Integer, Integer> board;
   HashMap<Integer, Integer> cloneboard;
   boolean turn = false; // false = player

   private int player;
   private int player1undo;
   private int player2undo;
   private boolean undocheck;

   /**
    * MancalaDataModel constructor initializes the data in the game
    */
   public MancalaDataModel() {
      arrayList = new ArrayList<ChangeListener>();
      board = new HashMap<Integer, Integer>();
      player = 1;
      player1undo = 3;
      player2undo = 3;
      undocheck = false;
      this.updateAllPits(3);
   }

   /**
    * getBoard returns the Hashmap of the board
    * @return Hashmap of data values of the pits
    */
   public HashMap<Integer, Integer> getBoard() {
      return board;
   }


   /**
    * UpdateAllPits sets up the player pits with a certain amount of stones
    * @param x int value of stones
    */
   public void updateAllPits(int x) {
      board.put(10, x);
      board.put(11, x);
      board.put(12, x);
      board.put(13, x);
      board.put(14, x);
      board.put(15, x);
      board.put(16, 0);

      board.put(20, x);
      board.put(21, x);
      board.put(22, x);
      board.put(23, x);
      board.put(24, x);
      board.put(25, x);
      board.put(26, 0);
   }

   /**
    * Rules method main method for mancala game
    * @param pit
    */
   public void rules(int pit) {
   
      // doesnt move if no stones
      if (board.get(pit) != 0) {
         // doesnt move on other pits
         if (player == 1 && pit > 19 && pit < 26 || player == 2 && pit > 9
               && pit < 16) {
            cloneboard = new HashMap<Integer, Integer>(board);// copy for
            // undo
            undocheck = true;
            int temp = ((Integer) board.get(pit));
            int index = pit + 1;
            int lastindex = 0;
            updatePit(pit, 0);
            for (int i = 0; i < temp; temp--) {
               if (player == 1 && index == 16 || index == 17)
                  index = 20;
               else if (player == 2 && index == 26 || index == 27)
                  index = 10;

               int temp2 = board.get(index) + 1;
               updatePit(index, temp2);
               lastindex = index;
               index++;
            }

            capture(lastindex);
            notifyListeners();

            nextTurn(lastindex);

            endGame();
         } else {
            JOptionPane.showMessageDialog(null, "Choose another pit!");

         }
      } else {
         JOptionPane.showMessageDialog(null, "Choose another pit!");
      }
   }

   /**
    * nextTurn checks if next players turn
    * @param lastindex int of the last stone position
    */
   public void nextTurn(int lastindex) {
      // Next players turn
      if (lastindex != 26 && lastindex != 16) {
         if (player == 1) {
            turn = false;
            player = 2;
         } else {
            player = 1;
            turn = true;
         }
      } else// free move
      {
         JOptionPane.showMessageDialog(null, "Free move!");
      }

   }

   /**
    * newGame method starts a new game
    */
   public void newGame() {
      int n = JOptionPane.showConfirmDialog(null,
            "Do you want to play again?", "Mancala",
            JOptionPane.YES_NO_OPTION);

      if (n == 0) {
         int stone = Integer.parseInt(JOptionPane
               .showInputDialog("Stones Per Pit:"));
         Object[] strategy = { "Normal", "Alternate" };
         int style = JOptionPane.showOptionDialog(null, "Choose a Style",
               "Style", JOptionPane.DEFAULT_OPTION,
               JOptionPane.PLAIN_MESSAGE, null, strategy, strategy[1]);

         updateAllPits(stone);

         notifyListeners();
      } else
         System.exit(0);

   }

   /**
    * capture method captures opponents stones
    * @param index int position of last stone placed
    */
   public void capture(int index) {

      // capture for player 1
      if (player == 1 && index > 19 && index < 26) {
         int pitsA = 20;
         int pitsB = 15;
         for (int i = 0; i < 6; i++) {
            if (index == pitsA && board.get(pitsA) == 1 && board.get(pitsB) != 0) {
               int pits = board.get(pitsB) + board.get(pitsA);
               updatePit(pitsA, 0);
               updatePit(pitsB, 0);
               updatePit(26, board.get(26) + pits);
               JOptionPane.showMessageDialog(null, "Player A Captures!");
            }
            pitsA++;
            pitsB--;
         }
      }

      // capture for player 2
      if (player == 2 && index > 9 && index < 16) {
         int pitsA = 20;
         int pitsB = 15;
         for (int i = 0; i < 6; i++) {
            if (index == pitsB && board.get(pitsB) == 1 && board.get(pitsA) != 0) {
               int pits = board.get(pitsB) + board.get(pitsA);
               updatePit(pitsA, 0);
               updatePit(pitsB, 0);
               updatePit(16, board.get(16) + pits);
               JOptionPane.showMessageDialog(null, "Player B Captures!");
            }
            pitsA++;
            pitsB--;
         }
      }
   }

   /**
    * Endgame method checks if game is over and if player wants to play again
    */
   public void endGame() {

      // end game
      if (board.get(20) == 0 && board.get(21) == 0 && board.get(22) == 0
            && board.get(23) == 0 && board.get(24) == 0
            && board.get(25) == 0) {
         int temp = board.get(10) + board.get(11) + board.get(12)
         + board.get(13) + board.get(14) + board.get(15);
         updatePit(16, board.get(16) + temp);
         winner();
      }

      if ((board.get(10) == 0 && board.get(11) == 0 && board.get(12) == 0
            && board.get(13) == 0 && board.get(14) == 0 && board.get(15) == 0)) {
         int temp = board.get(20) + board.get(21) + board.get(22)
         + board.get(23) + board.get(24) + board.get(25);
         updatePit(26, board.get(26) + temp);
         winner();
      }
   }

   /**
    * Winner method checks winners of game
    */
   public void winner() {
      if (board.get(26) < board.get(16))
         JOptionPane.showMessageDialog(null, "Player A Wins!");
      else if (board.get(26) > board.get(16)) {
         JOptionPane.showMessageDialog(null, "Player B Wins!");
      } else
         JOptionPane.showMessageDialog(null, "Tie Game!");
      updateAllPits(0);

      newGame();
   }

   public void tester() {
      Set set = board.entrySet();
      Iterator i = set.iterator();

      while (i.hasNext()) {
         Map.Entry me = (Map.Entry) i.next();
         System.out.print(me.getKey() + " " + me.getValue() + "|||");
      }

      System.out.println("Player: " + getPlayer());

   }

   /**
    * updatePit method change pit value of stones
    * @param key int value of pit position 
    * @param value int stone values
    */
   public void updatePit(int key, int value) {
      board.put(key, value);
   }

   /**
    * getPitCount returns amount of stones
    * @param key int value of pit positions
    * @return int of amount of stones
    */
   public int getPitCount(int key) {
      return board.get(key);
   }

   /**
    * getPlayer returns current player
    * @return String value of player
    */
   public String getPlayer() {
      if (player == 1)
         return "Player A";
      else
         return "Player B";

   }

   /**
    * undo method goes back to the previous move
    */
   public void undo() {
      if(player == 1){
      if (undocheck == true && player1undo > 0) {
         player1undo--;
         board = cloneboard;
         undocheck = false;
         if(player == 1)
            player = 2;
         else 
            player = 1;
         this.notifyListeners();
      } else
         JOptionPane.showMessageDialog(null, "Cannot Undo!");

      this.notifyListeners();
      }
      else 
         if (undocheck == true && player2undo > 0) {
            player2undo --;
            board = cloneboard;
            undocheck = false;
            if(player == 1)
               player = 2;
            else 
               player = 1;
            this.notifyListeners();
         } else
            JOptionPane.showMessageDialog(null, "Cannot Undo!");

         this.notifyListeners();
         }
   
   /**
    * getUndofirst returns first players undo
    * @return int value of undo
    */
   public int getUndofirst()
   {
   return player1undo;
   }

   /**
    * getUndoSec returns second players undo
    * @return int value of undo
    */
   public int getUndoSec()
   {
   return player2undo;
   }
   /**
    * attach method attaches the changelisteners
    * @param cl ChangeListeners
    */
   public void attach(ChangeListener cl) {
      arrayList.add(cl);
   }

   /**
    * notifyListeners notify when to update the data
    */
   public void notifyListeners() {
      for (ChangeListener x : arrayList) {
         x.stateChanged(null);
      }
     // System.out.println(arrayList.size());
   }

   /**
    * getTurn 
    * @return boolean false = player
    */
   public boolean getTurn() {
      return turn;
   }

}
